package com.cloudlead.utils.persistence.codegen.serializer;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.cloudlead.utils.persistence.codegen.PEntityType;
import com.cloudlead.utils.persistence.codegen.type.RepositoryImpl;
import com.mysema.codegen.CodeWriter;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Type;
import com.mysema.query.codegen.EntityType;
import com.mysema.query.codegen.Serializer;
import com.mysema.query.codegen.SerializerConfig;
import com.mysema.query.codegen.Supertype;

public class SimpleDaoImplSerializer implements Serializer {

	private final String javadocSuffix;

	private boolean printSupertype = true;

	/**
	 * Create a new BeanSerializer
	 */
	public SimpleDaoImplSerializer() {
		this(" is a Querydsl repository implement type");
	}

	/**
	 * Create a new BeanSerializer with the given javadoc suffix
	 * 
	 * @param javadocSuffix
	 */
	public SimpleDaoImplSerializer(String javadocSuffix) {
		this.javadocSuffix = javadocSuffix;
	}

	public void serialize(EntityType model, SerializerConfig serializerConfig,
			CodeWriter writer) throws IOException {
		String simpleName = model.getSimpleName();

		PEntityType entityType = (com.cloudlead.utils.persistence.codegen.PEntityType) model;
		ClassType superType = (ClassType) entityType.getParentType().getType();
		List<Type> parameters = superType.getParameters();

		// package
		if (!model.getPackageName().isEmpty()) {
			writer.packageDecl(model.getPackageName());
		}

		// header
		RepositoryImpl repAnnotation = new RepositoryImpl(
				Character.toLowerCase(simpleName.charAt(0))
						+ simpleName.substring(1, simpleName.length() - 4));
		model.addAnnotation(repAnnotation);

		// imports
		Set<String> importedClasses = getAnnotationTypes(model);
		if (model.hasLists()) {
			importedClasses.add(List.class.getName());
		}
		if (model.hasCollections()) {
			importedClasses.add(Collection.class.getName());
		}
		if (model.hasSets()) {
			importedClasses.add(Set.class.getName());
		}
		if (model.hasMaps()) {
			importedClasses.add(Map.class.getName());
		}
		importedClasses.add(Autowired.class.getName());
		importedClasses.add(DataSource.class.getName());
		importedClasses.add(Qualifier.class.getName());

		writer.importClasses(superType.getFullName());
		Type beanType = parameters.get(0);
		writer.importClasses(beanType.getFullName());
		importedClasses.add(model.getPackageName().substring(0,
				model.getPackageName().length() - 16)
				+ ".BusinessModules");
		writer.importClasses(model.getSuperType().getType().getFullName());
		writer.importClasses(importedClasses.toArray(new String[importedClasses
				.size()]));

		// javadoc
		writer.javadoc(simpleName + javadocSuffix);

		for (Annotation annotation : model.getAnnotations()) {
			writer.annotation(annotation);
		}

		Set<Supertype> superTypes = entityType.getSuperTypes();
		if (printSupertype && superTypes != null) {
			List<Type> ifcs = new ArrayList<>();
			for (Supertype sup : superTypes) {
				ifcs.add(sup.getType());
			}
			writer.beginClass(model, entityType.getParentType().getType(),
					ifcs.toArray(new Type[ifcs.size()]));
		} else {
			writer.beginClass(model);
		}

		bodyStart(model, writer);

		Set<Type> types = new HashSet<>();
		Type dsType = new ClassType(DataSource.class);
		types.add(dsType);
		addFullConstructor(model, writer, types, beanType);

		bodyEnd(model, writer);

		writer.end();
	}

	protected void bodyStart(EntityType model, CodeWriter writer)
			throws IOException {

	}

	protected void bodyEnd(EntityType model, CodeWriter writer)
			throws IOException {
		// template method
	}

	private Set<String> getAnnotationTypes(EntityType model) {
		Set<String> imports = new HashSet<String>();
		for (Annotation annotation : model.getAnnotations()) {
			imports.add(annotation.annotationType().getName());
		}
		return imports;
	}

	protected void addFullConstructor(EntityType model, CodeWriter writer,
			Set<Type> types, Type beanType) throws IOException {

		// full constructor

		writer.line("@Autowired");
		writer.line("public " + model.getSimpleName() + "(");
		writer.line("		@Qualifier(\"hasTranDataSource\") DataSource dataSource) {");
		writer.line("	super(", "BusinessModules.", beanType.getSimpleName().substring(1),
				", dataSource);");
		writer.line("}");
		writer.line("");
	}

	public void setPrintSupertype(boolean printSupertype) {
		this.printSupertype = printSupertype;
	}

}
